]> git.saurik.com Git - apple/security.git/blob - keychain/Signin Metrics/tests/SFSignInAnalyticsTests.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / Signin Metrics / tests / SFSignInAnalyticsTests.m
1 /*
2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import <XCTest/XCTest.h>
25 #import "keychain/Signin Metrics/SFSignInAnalytics.h"
26 #import "keychain/Signin Metrics/SFSignInAnalytics+Internal.h"
27 #import "keychain/ot/OTDefines.h"
28 #import "SFAnalytics+Signin.h"
29 #import <Security/SecureObjectSync/SOSCloudCircle.h>
30
31 static NSInteger _testnum;
32 static NSString* _path;
33
34 @interface SFSignInAnalyticsTester : SFSignInAnalytics
35 -(instancetype)init;
36 @end
37
38 @implementation SFSignInAnalyticsTester
39
40 + (NSString*)databasePath {
41 return _path;
42 }
43
44 -(instancetype)init
45 {
46 self = [super initWithSignInUUID:[NSUUID UUID].UUIDString category:@"CoreCDP" eventName:@"signin"];
47
48 return self;
49 }
50
51 @end
52
53
54 @interface SignInAnalyticsTests : XCTestCase
55 @property (nonatomic) SFSignInAnalyticsTester *metric;
56 @end
57
58 @implementation SignInAnalyticsTests
59
60
61 - (void)setUp {
62 [super setUp];
63 _testnum = 0;
64 self.continueAfterFailure = NO;
65 _path = [@"/tmp" stringByAppendingFormat:@"/test_%ld.db", (long)++_testnum];
66 _metric = [[SFSignInAnalyticsTester alloc] init];
67 XCTAssertNotNil(_metric, "SignInAnalyticsTester object should not be nil");
68 }
69
70 - (void)tearDown
71 {
72 dispatch_async([SFSIALoggerObject logger].queue, ^{
73 [[SFSIALoggerObject logger].database executeSQL:@"delete from all_events"];
74 [[SFSIALoggerObject logger].database executeSQL:@"delete from soft_failures"];
75 [[SFSIALoggerObject logger].database executeSQL:@"delete from hard_failures"];
76 });
77
78 [[SFSIALoggerObject logger] removeState];
79 _metric = nil;
80 [super tearDown];
81 }
82
83 - (void)testStop
84 {
85 sleep(2);
86 NSDictionary *attributes = @{@"success": @YES,
87 @"takenFlow" : @"restore",
88 };
89 XCTAssertNotNil(attributes, "attributes dictionary should exist");
90
91 [_metric stopWithAttributes:attributes];
92
93 NSArray* results = [[SFSIALoggerObject logger].database allEvents];
94
95 XCTAssertEqual([results count], 2, @"should have 2 results");
96
97 }
98
99 - (void)testCancel
100 {
101 [_metric cancel];
102 }
103
104 - (void)testLogError
105 {
106 NSError* error = [NSError errorWithDomain:OctagonErrorDomain code:OTErrorBottleID userInfo:@{NSLocalizedDescriptionKey: @"Failed to deserialize bottle peer"}];
107 [_metric logRecoverableError:error];
108 NSArray* results = [[SFSIALoggerObject logger].database softFailures];
109 XCTAssertEqual([results count], 1, @"should have 1 results");
110 }
111
112 - (void)testCreateNewSubtask
113 {
114 SFSignInAnalytics* child = [_metric newSubTaskForEvent:@"restore"];
115 XCTAssertNotNil(child, "child should be created");
116 [[SFSIALoggerObject logger] removeState];
117 child = nil;
118 }
119
120
121 - (void)testCreateNewSubtaskAndStop
122 {
123 SFSignInAnalytics* child = [_metric newSubTaskForEvent:@"restore"];
124
125 sleep(2);
126 NSDictionary *attributes = @{@"success": @YES,
127 @"takenFlow" : @"piggyback",
128 };
129
130 [child stopWithAttributes:attributes];
131
132 XCTAssertNotNil(child, "child should be created");
133
134 NSArray* results = [[SFSIALoggerObject logger].database allEvents];
135
136 XCTAssertEqual([results count], 2, @"should have 2 results");
137
138 [[SFSIALoggerObject logger] removeState];
139 child = nil;
140 }
141
142 - (void)testStopAfterCancel
143 {
144 sleep(2);
145 NSDictionary *attributes = @{@"success": @YES,
146 @"takenFlow" : @"piggyback",
147 };
148 XCTAssertNotNil(attributes, "attributes dictionary should exist");
149
150 [_metric cancel];
151
152 [_metric stopWithAttributes:attributes];
153
154 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
155
156 XCTAssertEqual([allEvents count], 0, @"should have 0 things logged");
157 }
158
159 - (void)testStopAfterStop
160 {
161 sleep(2);
162 NSDictionary *attributes = @{@"success": @YES,
163 @"takenFlow" : @"piggyback",
164 };
165 XCTAssertNotNil(attributes, "attributes dictionary should exist");
166
167 [_metric stopWithAttributes:attributes];
168
169 [_metric stopWithAttributes:attributes];
170
171 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
172
173 XCTAssertEqual([allEvents count], 2, @"should have 2 things logged");
174 }
175
176 -(void)testSignInComplete
177 {
178 NSDictionary* attributes = [NSDictionary dictionary];
179 [_metric stopWithAttributes:attributes];
180
181 [_metric signInCompleted];
182
183 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
184 XCTAssertNotNil(allEvents, "array should not be nil");
185 XCTAssertTrue(allEvents && [allEvents count] > 0, "array should not be nil and contain an entry");
186
187 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
188 XCTAssertTrue(dependencyEntry && [dependencyEntry count] > 0, "dictionary should not be nil and contain an entry");
189
190 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
191
192 XCTAssertEqual([chains count], 1, "should be one list");
193
194 XCTAssertTrue([chains containsObject:_metric.signin_uuid], "should contain 1 uuid");
195 }
196
197 -(void)testSingleChainDependencyList
198 {
199 SFSignInAnalytics* child1 = [_metric newSubTaskForEvent:@"piggyback"];
200 XCTAssertNotNil(child1, "child1 should be created");
201
202 SFSignInAnalytics* child2 = [child1 newSubTaskForEvent:@"initialsync"];
203 XCTAssertNotNil(child2, "child2 should be created");
204
205 SFSignInAnalytics* child3 = [child2 newSubTaskForEvent:@"backup"];
206 XCTAssertNotNil(child3, "child3 should be created");
207
208 SFSignInAnalytics* child4 = [child3 newSubTaskForEvent:@"processing one ring"];
209 XCTAssertNotNil(child4, "child4 should be created");
210
211 [_metric signInCompleted];
212
213 NSString *expectedChain = [NSString stringWithFormat:@"%@, %@, %@, %@, %@", child1.signin_uuid, child1.my_uuid, child2.my_uuid, child3.my_uuid, child4.my_uuid];
214
215 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
216 XCTAssertNotNil(allEvents, "should not be nil");
217 XCTAssertTrue([allEvents count] > 0, "should be events");
218
219 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
220 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
221
222 XCTAssertEqual([chains count], 1, "should be one list");
223 XCTAssertTrue([expectedChain isEqualToString:[chains objectAtIndex:0]], "chains should be the same");
224
225 child1 = nil;
226 child2 = nil;
227 child3 = nil;
228 child4 = nil;
229
230 child1 = nil;
231 child2 = nil;
232 child3 = nil;
233 child4 = nil;
234 }
235
236 -(void)testMultipleChildrenPerEvent
237 {
238 SFSignInAnalytics* child1 = [_metric newSubTaskForEvent:@"piggyback"];
239 XCTAssertNotNil(child1, "child1 should be created");
240
241 SFSignInAnalytics* child2 = [child1 newSubTaskForEvent:@"initialsync"];
242 XCTAssertNotNil(child2, "child2 should be created");
243
244 SFSignInAnalytics* child3 = [child1 newSubTaskForEvent:@"backup"];
245 XCTAssertNotNil(child3, "child3 should be created");
246
247 SFSignInAnalytics* child4 = [child1 newSubTaskForEvent:@"processing one ring"];
248 XCTAssertNotNil(child4, "child4 should be created");
249
250 SFSignInAnalytics* child5 = [child2 newSubTaskForEvent:@"processing second ring"];
251 XCTAssertNotNil(child5, "child5 should be created");
252
253 SFSignInAnalytics* child6 = [child2 newSubTaskForEvent:@"processing third ring"];
254 XCTAssertNotNil(child6, "child6 should be created");
255
256 SFSignInAnalytics* child7 = [child2 newSubTaskForEvent:@"processing fourth ring"];
257 XCTAssertNotNil(child7, "child7 should be created");
258
259 SFSignInAnalytics* child8 = [child7 newSubTaskForEvent:@"processing fifth ring"];
260 XCTAssertNotNil(child8, "child8 should be created");
261
262 SFSignInAnalytics* child9 = [child7 newSubTaskForEvent:@"processing one ring"];
263 XCTAssertNotNil(child9, "child9 should be created");
264
265 NSString *expectedChain = [NSString stringWithFormat:@"%@, %@, %@, %@", _metric.signin_uuid, child1.my_uuid, child2.my_uuid, child5.my_uuid];
266
267 NSString *expectedChain1 = [NSString stringWithFormat:@"%@, %@, %@", _metric.signin_uuid, child1.my_uuid, child3.my_uuid];
268
269 NSString *expectedChain2 = [NSString stringWithFormat:@"%@, %@, %@", _metric.signin_uuid, child1.my_uuid, child4.my_uuid];
270
271 NSString *expectedChain3 = [NSString stringWithFormat:@"%@, %@, %@, %@", _metric.signin_uuid, child1.my_uuid, child2.my_uuid, child5.my_uuid];
272
273 NSString *expectedChain4 = [NSString stringWithFormat:@"%@, %@, %@, %@, %@", _metric.signin_uuid, child1.my_uuid, child2.my_uuid, child7.my_uuid, child8.my_uuid];
274
275 NSString *expectedChain5 = [NSString stringWithFormat:@"%@, %@, %@, %@, %@", _metric.signin_uuid, child1.my_uuid, child2.my_uuid, child7.my_uuid, child9.my_uuid];
276
277
278 [_metric signInCompleted];
279
280 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
281 XCTAssertNotNil(allEvents, "array is not nil");
282 XCTAssertTrue([allEvents count] > 0, "array should not be empty");
283
284 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
285 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
286
287 XCTAssertEqual([chains count], 6, "should be one list");
288
289 XCTAssertTrue([chains containsObject:expectedChain], "chains should contain expectedChain");
290 XCTAssertTrue([chains containsObject:expectedChain1], "chains should contain expectedChain1");
291 XCTAssertTrue([chains containsObject:expectedChain2], "chains should contain expectedChain2");
292 XCTAssertTrue([chains containsObject:expectedChain3], "chains should contain expectedChain3");
293 XCTAssertTrue([chains containsObject:expectedChain4], "chains should contain expectedChain4");
294 XCTAssertTrue([chains containsObject:expectedChain5], "chains should contain expectedChain5");
295
296 [[SFSIALoggerObject logger] removeState];
297
298 child1 = nil;
299 child2 = nil;
300 child3 = nil;
301 child4 = nil;
302 child5 = nil;
303 child6 = nil;
304 child7 = nil;
305 child8 = nil;
306 child9 = nil;
307
308 }
309
310 -(void)testSOSCCWaitForInitialSync
311 {
312 CFErrorRef error = nil;
313 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
314 XCTAssertNotNil(archiver, "should not be nil");
315 [_metric encodeWithCoder:archiver];
316 CFDataRef parentData = (__bridge CFDataRef)archiver.encodedData;
317 XCTAssertNotNil((__bridge NSData*)parentData, "should not be nil");
318 bool worked = SOSCCWaitForInitialSyncWithAnalytics(parentData, &error);
319 XCTAssertTrue(worked, "should have worked");
320
321 [_metric signInCompleted];
322
323 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
324 XCTAssertNotNil(allEvents, "array is not nil");
325 XCTAssertEqual([allEvents count], 1, "array should not contain 1 entry");
326
327 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
328 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
329 XCTAssertNotNil(chains, "chains is not nil");
330 XCTAssertEqual([chains count], 1, "array should not contain 1 entry");
331 }
332
333 -(void)testSOSCCRemoveThisDeviceFromCircle
334 {
335 CFErrorRef error = nil;
336 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
337 XCTAssertNotNil(archiver, "should not be nil");
338 [_metric encodeWithCoder:archiver];
339 CFDataRef parentData = (__bridge CFDataRef)archiver.encodedData;
340 XCTAssertNotNil((__bridge NSData*)parentData, "should not be nil");
341 bool worked = SOSCCRemoveThisDeviceFromCircleWithAnalytics(parentData, &error);
342 XCTAssertTrue(worked, "should have worked");
343
344 [_metric signInCompleted];
345
346 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
347 XCTAssertNotNil(allEvents, "array is not nil");
348 XCTAssertEqual([allEvents count], 1, "array should not contain 1 entry");
349
350 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
351 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
352 XCTAssertNotNil(chains, "chains is not nil");
353 XCTAssertEqual([chains count], 1, "array should not contain 1 entry");
354 }
355
356 -(void)testSOSCCRequestToJoinCircle
357 {
358 CFErrorRef error = nil;
359 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
360 XCTAssertNotNil(archiver, "should not be nil");
361 [_metric encodeWithCoder:archiver];
362 CFDataRef parentData = (__bridge CFDataRef)archiver.encodedData;
363 XCTAssertNotNil((__bridge NSData*)parentData, "should not be nil");
364 SOSCCRequestToJoinCircleWithAnalytics(parentData, &error);
365
366 [_metric signInCompleted];
367
368 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
369 XCTAssertNotNil(allEvents, "array is not nil");
370 XCTAssertEqual([allEvents count], 1, "array should not contain 1 entry");
371
372 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
373 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
374 XCTAssertNotNil(chains, "chains is not nil");
375 XCTAssertEqual([chains count], 1, "array should not contain 1 entry");
376 }
377
378 -(void)testSOSCCRequestToJoinCircleAfterRestore
379 {
380 CFErrorRef error = nil;
381 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
382 XCTAssertNotNil(archiver, "should not be nil");
383 [_metric encodeWithCoder:archiver];
384 CFDataRef parentData = (__bridge CFDataRef)archiver.encodedData;
385 XCTAssertNotNil((__bridge NSData*)parentData, "should not be nil");
386 SOSCCRequestToJoinCircleAfterRestoreWithAnalytics(parentData, &error);
387
388 [_metric signInCompleted];
389
390 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
391 XCTAssertNotNil(allEvents, "array is not nil");
392 XCTAssertEqual([allEvents count], 1, "array should not contain 1 entry");
393
394 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
395 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
396 XCTAssertNotNil(chains, "chains is not nil");
397 XCTAssertEqual([chains count], 1, "array should not contain 1 entry");
398 }
399
400 -(void)testSOSCCRemovePeersFromCircle
401 {
402 CFErrorRef error = nil;
403 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
404 XCTAssertNotNil(archiver, "should not be nil");
405 [_metric encodeWithCoder:archiver];
406 CFDataRef parentData = (__bridge CFDataRef)archiver.encodedData;
407 XCTAssertNotNil((__bridge NSData*)parentData, "should not be nil");
408 NSArray* peers = [NSArray array];
409 SOSCCRemovePeersFromCircleWithAnalytics((__bridge CFArrayRef)peers, parentData, &error);
410
411 [_metric signInCompleted];
412
413 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
414 XCTAssertNotNil(allEvents, "array is not nil");
415 XCTAssertEqual([allEvents count], 1, "array should not contain 1 entry");
416
417 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
418 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
419 XCTAssertNotNil(chains, "chains is not nil");
420 XCTAssertEqual([chains count], 1, "array should not contain 1 entry");
421 }
422
423
424 -(void)testSOSCCViewSet
425 {
426 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
427 XCTAssertNotNil(archiver, "should not be nil");
428 [_metric encodeWithCoder:archiver];
429 CFDataRef parentData = (__bridge CFDataRef)archiver.encodedData;
430 XCTAssertNotNil((__bridge NSData*)parentData, "should not be nil");
431 CFSetRef enabledViews = nil;
432 CFSetRef disabledViews = nil;
433 SOSCCViewSetWithAnalytics(enabledViews, disabledViews, parentData);
434
435 [_metric signInCompleted];
436
437 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
438 XCTAssertNotNil(allEvents, "array is not nil");
439 XCTAssertEqual([allEvents count], 1, "array should not contain 1 entry");
440
441 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
442 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
443 XCTAssertNotNil(chains, "chains is not nil");
444 XCTAssertEqual([chains count], 1, "array should not contain 1 entry");
445 }
446
447 -(void)testSOSCCSetUserCredentialsAndDSID
448 {
449 CFErrorRef error = nil;
450 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
451 XCTAssertNotNil(archiver, "should not be nil");
452 [_metric encodeWithCoder:archiver];
453 CFDataRef parentData = (__bridge CFDataRef)archiver.encodedData;
454 XCTAssertNotNil((__bridge NSData*)parentData, "should not be nil");
455 CFStringRef label = nil;
456 CFDataRef password = nil;
457 CFStringRef dsid = nil;
458 SOSCCSetUserCredentialsAndDSIDWithAnalytics(label, password, dsid, parentData, &error);
459
460 [_metric signInCompleted];
461
462 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
463 XCTAssertNotNil(allEvents, "array is not nil");
464 XCTAssertEqual([allEvents count], 1, "array should not contain 1 entry");
465
466 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
467 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
468 XCTAssertNotNil(chains, "chains is not nil");
469 XCTAssertEqual([chains count], 1, "array should not contain 1 entry");
470 }
471
472 -(void)testSOSCCResetToEmpty
473 {
474 CFErrorRef error = nil;
475 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
476 XCTAssertNotNil(archiver, "should not be nil");
477 [_metric encodeWithCoder:archiver];
478 CFDataRef parentData = (__bridge CFDataRef)archiver.encodedData;
479 XCTAssertNotNil((__bridge NSData*)parentData, "should not be nil");
480 SOSCCResetToEmptyWithAnalytics(parentData, &error);
481
482 [_metric signInCompleted];
483
484 NSArray *allEvents = [[SFSIALoggerObject logger].database allEvents];
485 XCTAssertNotNil(allEvents, "array is not nil");
486 XCTAssertEqual([allEvents count], 1, "array should not contain 1 entry");
487
488 NSDictionary *dependencyEntry = [allEvents objectAtIndex:[allEvents count]-1];
489 NSArray *chains = [dependencyEntry objectForKey:@"dependencyChains"];
490 XCTAssertNotNil(chains, "chains is not nil");
491 XCTAssertEqual([chains count], 1, "array should not contain 1 entry");
492 }
493
494 - (void)testMultipleDBConnections
495 {
496 NSError* error = [NSError errorWithDomain:OctagonErrorDomain code:OTErrorBottleID userInfo:@{NSLocalizedDescriptionKey: @"Failed to deserialize bottle peer"}];
497 dispatch_queue_t test_queue = dispatch_queue_create("com.apple.security.signin.tests", DISPATCH_QUEUE_CONCURRENT_WITH_AUTORELEASE_POOL);
498
499 for(int i = 0; i < 1000; i++){
500 SFSignInAnalytics *test1 = [_metric newSubTaskForEvent:@"connection1"];
501 SFSignInAnalytics *test2 = [_metric newSubTaskForEvent:@"connection2"];
502
503 dispatch_async(test_queue, ^{
504 [test1 logRecoverableError:error];
505 });
506 dispatch_async(test_queue, ^{
507 [test2 stopWithAttributes:nil];
508 });
509 dispatch_async(test_queue, ^{
510 [self->_metric logRecoverableError:error];
511 });
512 }
513 }
514 @end